package com.atguigu.juc.locks;

import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;

/**
 * @author yanglin
 * @create 2024-01-06 17:08 星期六
 * description:
 */
public class Lock8Demo {


    /**
     * 线程  操作 资源类
     *  8锁案例：
     *      1、标准访问两个线程，请问先打印邮件还是短信    //邮件  短信
     *      2、sendEmail中加入暂停3秒，请问先打印邮件还是短信    //邮件  短信
     *      3、添加一个普通方法hello，请问先打印邮件还是先打印hello   //hello 邮件
     *      4、有两部手机，请问先打印邮件还是短信   //短信    邮件
     *      5、有两个静态同步方法，有1部手机请问先打印邮件还是短信      //邮件   短信
     *      6、有两个静态同步方法，有2部手机请问先打印邮件还是短信      //邮件    短信
     *      7、有一个静态同步方法，有一个普通同步方法，有1部手机，请问先打印邮件还是短信    //短信  邮件
     *      8、有一个静态同步方法，有一个普通同步方法，有2部手机，请问先打印邮件还是短信    //短信   邮件
     *      
     * 总结：
     * 1-2
     *  一个对象里面如果有多个synchronized方法，某一时刻内，只要一个线程去调用其中一个synchronized方法了，
     *  其它的线程都只能等待，换句话说，某一时刻内，只能有唯一的一个线程去访问这个synchronized方法，
     *  锁的是当前对象this，别锁定后，其他的线程都不能进入到当前对象的其他synchronized方法
     *  
     * 3-4
     *  加入普通方法后发现和同步锁无关
     *  换成两个对象后，使用的不是同一把锁了，情况立刻发生变化。
     *  
     * 5-6：都换成静态同步方法后，情况又会发生变化
     *  三种synchronized 锁的内容有一些差别：
     *   对于普通同步方法，锁的是当前实例对象，通常是指this，具体的一部手机，所有的普通同步方法都用的是同一把锁 --》实例对象本身
     *   对于静态同步方法，锁的是当前的Class对象，如Phone.class唯一的一个模板
     *   对于同步方法快，锁的是synchronized括号内的对象
     *   
     * 7-8：
     *  当一个线程试图访问同步代码时，它首先必须得到锁，正常退出或抛出异常时必须释放锁
     *  所有的普通同步方法用的都是同一把锁--》实例对象本身，就是new出来的具体实例对象本身，本类this
     *  也就是说一个实例对象的普通同步方法获取到锁后，该实例对象的其他普通同步方法必须等获取到锁的对象释放锁资源后才能够获取
     *  所有的静态同步方法用的也是同一把锁--》类对象本身，就是我们所说的唯一模板Class
     *  具体实例对象this和唯一模板Class，这两把锁是不同的对象，所有静态同步方法和普通同步方法之间不会有锁的竞争状态
     *  但是一旦一个静态同步方法获取锁后，其他的静态同步方法都必须等待这个静态同步方法释放锁后才能够获取
     *  
     */
    @Test
    public void testLock8Demo()  {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        },"a").start();

        //保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
//            phone.sendSMS();
//            phone.hello();
            phone2.sendSMS();
        },"b").start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
    
}

class Phone {
    
    public static synchronized void sendEmail(){
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("send email....");
    }

    public synchronized void sendSMS(){
        System.out.println("send SMS....");
    }
    
    public void hello(){
        System.out.println("hello");
    }
}
